package com.gf.framework.mysql.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gf.framework.common.exception.BizException;
import com.gf.framework.common.model.SimpleConditionDto;
import com.gf.framework.common.model.SimplePageDto;
import com.gf.framework.common.model.SimpleUpdateDto;
import com.gf.framework.idgenetor.config.IdGenetorConfig;
import com.gf.framework.idgenetor.config.SnowFlakeIdGentetor;
import com.gf.framework.mysql.entity.BaseEntity;
import com.gf.framework.mysql.service.ISnowService;
import com.gf.framework.mysql.service.SnowServiceImpl;
import com.google.common.collect.ArrayListMultimap;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author gaofei
 * @date 2022/5/22 21:44
 */
public abstract class MysqlCrudController<T extends BaseEntity, M extends BaseMapper<T>,
        SERVICE extends ISnowService<T>> implements SmartInitializingSingleton {
    @Autowired
    protected SERVICE service;

    private final HashMap<String, String> propertyColumMap = new HashMap<>();
    private final HashMap<String, Field> fieldHashMap = new HashMap<>();

    /**
     * 是否使用数据库自增的id
     * @return
     */
    protected abstract boolean autoId();

    @Override
    public void afterSingletonsInstantiated() {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(service.getEntityClass());
        for (TableFieldInfo tableFieldInfo : tableInfo.getFieldList()) {
            propertyColumMap.put(tableFieldInfo.getProperty(), tableFieldInfo.getColumn());
        }
        propertyColumMap.put(tableInfo.getKeyProperty(), tableInfo.getKeyColumn());

        Class<?> classzz = service.getEntityClass();
        while (classzz.getSuperclass() != null) {
            Field[] fields = classzz.getDeclaredFields();
            for (Field field : fields) {
                if (propertyColumMap.containsKey(field.getName())) {
                    fieldHashMap.put(field.getName(), field);
                }
            }
            classzz = classzz.getSuperclass();
        }
    }

    @ApiOperation("新增或保存数据，成功返回保存的id")
    @PostMapping("save")
    public Long save(@RequestBody T data) {
        if (!autoId()) {
            data.setId(service.nextId());
        }

        if (service.save(data)) {
            return data.getId();
        }
        return null;
    }

    @ApiOperation("通过id更新数据")
    @PostMapping("updatebyid")
    public Boolean updateById(@RequestBody T data) {
        return service.updateById(data);
    }

    @ApiOperation("通过id删除")
    @DeleteMapping("delbyid/{id}")
    public Boolean deleteById(@PathVariable("id")Long id) {
        return service.removeById(id);
    }

    @ApiOperation("根据id获取")
    @GetMapping("getbyid/{id}")
    public T queryById(@PathVariable("id")Long id) {
        return service.getById(id);
    }

    @ApiOperation("根据简单条件查询")
    @PostMapping("simplequery")
    public Page<T> simplequery(@RequestBody SimplePageDto simplePageDto) throws NoSuchFieldException {
        QueryWrapper<T> queryWrapper = queryStructure(simplePageDto);
        queryWrapper = orderByQueryWrapper(queryWrapper, simplePageDto.getOrder());
        return service.page(new Page<>(simplePageDto.getPageIndex(), simplePageDto.getLength()), queryWrapper);
    }

    @ApiOperation("通过条件更新")
    @PostMapping("update")
    public Boolean update(@RequestBody SimpleUpdateDto simpleUpdateDto) throws NoSuchFieldException {
        UpdateWrapper<T> updateWrapper = updateStructure(simpleUpdateDto);

        for (Map.Entry<String, String> entry : simpleUpdateDto.getUpdate().entrySet()) {
            updateWrapper.set(getColumName(entry.getKey()), toObj(entry.getKey(), entry.getValue()));
        }

        updateWrapper.set("update_time", LocalDateTime.now());

        return service.update(updateWrapper);
    }

    @ApiOperation("通过条件删除")
    @PostMapping("delete")
    public Boolean delete(
            @RequestBody SimpleConditionDto simpleConditionDto) throws NoSuchFieldException {
        return service.remove(queryStructure(simpleConditionDto));
    }


    private UpdateWrapper<T> updateStructure(
            SimpleConditionDto simpleConditionDto) throws NoSuchFieldException {
        // 构建条件
        UpdateWrapper<T> wrapper = new UpdateWrapper<>();
        wrapper = inStructure(wrapper, simpleConditionDto.getIn());
        wrapper = eqStructure(wrapper, simpleConditionDto.getEq());
        wrapper = ltStructure(wrapper, simpleConditionDto.getLt());
        wrapper = lteStructure(wrapper, simpleConditionDto.getLte());
        wrapper = gtStructure(wrapper, simpleConditionDto.getGt());
        wrapper = gteStructure(wrapper, simpleConditionDto.getGte());
        return wrapper;
    }


    private QueryWrapper<T> queryStructure(
            SimpleConditionDto simpleConditionDto) throws NoSuchFieldException {
        // 构建条件
        QueryWrapper<T> wrapper = new QueryWrapper<>();
        wrapper = inStructure(wrapper, simpleConditionDto.getIn());
        wrapper = eqStructure(wrapper, simpleConditionDto.getEq());
        wrapper = ltStructure(wrapper, simpleConditionDto.getLt());
        wrapper = lteStructure(wrapper, simpleConditionDto.getLte());
        wrapper = gtStructure(wrapper, simpleConditionDto.getGt());
        wrapper = gteStructure(wrapper, simpleConditionDto.getGte());
        return wrapper;
    }

    protected QueryWrapper<T> orderByQueryWrapper(QueryWrapper<T> queryWrapper,
                                                  String sort) {
        if (sort != null && !sort.isEmpty()) {
            String[] sorts = sort.split("#");
            for (String sortStr : sorts) {
                String[] sortInfo = sortStr.split(":");
                String columName = getColumName(sortInfo[1]);
                if ("asc".equalsIgnoreCase(sortInfo[0])) {
                    queryWrapper = queryWrapper.orderByAsc(columName);
                }
                else if ("desc".equalsIgnoreCase(sortInfo[0])) {
                    queryWrapper = queryWrapper.orderByDesc(columName);
                }
            }
        }

        return queryWrapper;
    }
    /**
     * in 条件构建
     * @param wrapper
     * @param in
     * @return
     */
    private QueryWrapper<T> inStructure(QueryWrapper<T> wrapper, ArrayListMultimap<String, String> in) {
        if (in == null || in.isEmpty()) {
            return wrapper;
        }

        in.asMap().forEach((key, value) -> wrapper.in(getColumName(key), value));
        return wrapper;
    }
    private UpdateWrapper<T> inStructure(UpdateWrapper<T> wrapper, ArrayListMultimap<String, String> in) {
        if (in == null || in.isEmpty()) {
            return wrapper;
        }

        in.asMap().forEach((key, value) -> wrapper.in(getColumName(key), value));
        return wrapper;
    }

    /**
     * 相等条件构建
     * @param wrapper
     * @param eq
     * @return
     * @throws NoSuchFieldException
     */
    private QueryWrapper<T> eqStructure(QueryWrapper<T> wrapper, Map<String, String> eq) throws NoSuchFieldException {
        if (eq == null || eq.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : eq.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.eq(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }
    private UpdateWrapper<T> eqStructure(UpdateWrapper<T> wrapper, Map<String, String> eq) throws NoSuchFieldException {
        if (eq == null || eq.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : eq.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.eq(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }

    /**
     * 小于条件构建
     * @param wrapper
     * @param lt
     * @return
     * @throws NoSuchFieldException
     */
    private QueryWrapper<T> ltStructure(QueryWrapper<T> wrapper, Map<String, String> lt) throws NoSuchFieldException {
        if (lt == null || lt.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : lt.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.lt(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }
    private UpdateWrapper<T> ltStructure(UpdateWrapper<T> wrapper, Map<String, String> lt) throws NoSuchFieldException {
        if (lt == null || lt.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : lt.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.lt(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }


    /**
     * 小于等于条件构建
     * @param wrapper
     * @param lte
     * @return
     * @throws NoSuchFieldException
     */
    private QueryWrapper<T> lteStructure(QueryWrapper<T> wrapper, Map<String, String> lte) throws NoSuchFieldException {
        if (lte == null || lte.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : lte.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.le(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }
    private UpdateWrapper<T> lteStructure(UpdateWrapper<T> wrapper, Map<String, String> lte) throws NoSuchFieldException {
        if (lte == null || lte.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : lte.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.le(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }

    /**
     * 大于条件构建
     * @param wrapper
     * @param gt
     * @return
     * @throws NoSuchFieldException
     */
    private QueryWrapper<T> gtStructure(QueryWrapper<T> wrapper, Map<String, String> gt) throws NoSuchFieldException {
        if (gt == null || gt.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : gt.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.gt(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }
    private UpdateWrapper<T> gtStructure(UpdateWrapper<T> wrapper, Map<String, String> gt) throws NoSuchFieldException {
        if (gt == null || gt.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : gt.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.gt(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }

    /**
     * 大于等于条件构建
     * @param wrapper
     * @param gte
     * @return
     * @throws NoSuchFieldException
     */
    private QueryWrapper<T> gteStructure(QueryWrapper<T> wrapper, Map<String, String> gte) throws NoSuchFieldException {
        if (gte == null || gte.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : gte.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.ge(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }
    private UpdateWrapper<T> gteStructure(UpdateWrapper<T> wrapper, Map<String, String> gte) throws NoSuchFieldException {
        if (gte == null || gte.isEmpty()) {
            return wrapper;
        }

        for (Map.Entry<String, String> entry : gte.entrySet()) {
            String columName = getColumName(entry.getKey());
            wrapper.ge(columName, toObj(entry.getKey(), entry.getValue()));
        }
        return wrapper;
    }

    /**
     * 参数转换
     * @param columName
     * @param value
     * @return
     * @throws NoSuchFieldException
     */
    private Object toObj(String columName, String value) throws NoSuchFieldException {
        Field field = fieldHashMap.get(columName);
        if (field == null) {
            throw new BizException(String.format("无法映射的属性{%s}", columName));
        }
        if (field.getType().equals(Byte.class) || field.getType().equals(Short.class) || field.getType().equals(Integer.class) ||
                field.getType().equals(Long.class) || field.getType().equals(Float.class) || field.getType().equals(Double.class)) {
            return Double.parseDouble(value);
        }
        else if (field.getType().equals(String.class)) {
            return value;
        }
        else if (field.getType().equals(LocalDate.class)) {
            DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            return LocalDate.parse(value, format);
        }
        else if (field.getType().equals(LocalDateTime.class)) {
            DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return LocalDateTime.parse(value, format);
        }
        throw new BizException("不支持的条件类型");
    }

    private String getColumName(String property) {
        if (!propertyColumMap.containsKey(property)) {
            throw new BizException(String.format("无法映射的属性{%s}", property));
        }
        return propertyColumMap.get(property);
    }
}
